/*     */ package com.zimbra.cs.dav.service;
/*     */ 
/*     */ import com.zimbra.client.ZFolder;
/*     */ import com.zimbra.client.ZMailbox;
/*     */ import com.zimbra.client.ZMailbox.Options;
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.httpclient.HttpClientUtil;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.HttpUtil;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.Log.Level;
/*     */ import com.zimbra.common.util.Pair;
/*     */ import com.zimbra.common.util.ZimbraHttpConnectionManager;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.AuthToken;
/*     */ import com.zimbra.cs.account.AuthTokenException;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Server;
/*     */ import com.zimbra.cs.dav.DavContext;
/*     */ import com.zimbra.cs.dav.DavContext.Depth;
/*     */ import com.zimbra.cs.dav.DavContext.KnownUserAgent;
/*     */ import com.zimbra.cs.dav.DavElements;
/*     */ import com.zimbra.cs.dav.DavException;
/*     */ import com.zimbra.cs.dav.DomUtil;
/*     */ import com.zimbra.cs.dav.service.method.Acl;
/*     */ import com.zimbra.cs.dav.service.method.Copy;
/*     */ import com.zimbra.cs.dav.service.method.Delete;
/*     */ import com.zimbra.cs.dav.service.method.Get;
/*     */ import com.zimbra.cs.dav.service.method.Head;
/*     */ import com.zimbra.cs.dav.service.method.Lock;
/*     */ import com.zimbra.cs.dav.service.method.MkCalendar;
/*     */ import com.zimbra.cs.dav.service.method.MkCol;
/*     */ import com.zimbra.cs.dav.service.method.Move;
/*     */ import com.zimbra.cs.dav.service.method.Options;
/*     */ import com.zimbra.cs.dav.service.method.Post;
/*     */ import com.zimbra.cs.dav.service.method.PropFind;
/*     */ import com.zimbra.cs.dav.service.method.PropPatch;
/*     */ import com.zimbra.cs.dav.service.method.Put;
/*     */ import com.zimbra.cs.dav.service.method.Report;
/*     */ import com.zimbra.cs.dav.service.method.Unlock;
/*     */ import com.zimbra.cs.mailbox.Folder;
/*     */ import com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.MailboxManager;
/*     */ import com.zimbra.cs.mailbox.Mountpoint;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.AccountCtags;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.AccountKey;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.CalendarCacheManager;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.CtagInfo;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.CtagResponseCache;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.CtagResponseCache.CtagResponseCacheKey;
/*     */ import com.zimbra.cs.mailbox.calendar.cache.CtagResponseCache.CtagResponseCacheValue;
/*     */ import com.zimbra.cs.memcached.MemcachedConnector;
/*     */ import com.zimbra.cs.service.AuthProvider;
/*     */ import com.zimbra.cs.service.FileUploadServlet.Upload;
/*     */ import com.zimbra.cs.service.util.ItemId;
/*     */ import com.zimbra.cs.servlet.ZimbraServlet;
/*     */ import com.zimbra.cs.servlet.util.AuthUtil;
/*     */ import com.zimbra.cs.servlet.util.AuthUtil.AuthResult;
/*     */ import com.zimbra.cs.util.AccountUtil;
/*     */ import java.io.ByteArrayInputStream;
/*     */ import java.io.ByteArrayOutputStream;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.util.Collection;
/*     */ import java.util.Enumeration;
/*     */ import java.util.HashMap;
/*     */ import java.util.Iterator;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ import java.util.zip.GZIPInputStream;
/*     */ import java.util.zip.GZIPOutputStream;
/*     */ import javax.servlet.ServletException;
/*     */ import javax.servlet.ServletOutputStream;
/*     */ import javax.servlet.http.HttpServletRequest;
/*     */ import javax.servlet.http.HttpServletResponse;
/*     */ import org.apache.commons.httpclient.Header;
/*     */ import org.apache.commons.httpclient.HttpClient;
/*     */ import org.apache.commons.httpclient.HttpMethod;
/*     */ import org.apache.commons.httpclient.HttpState;
/*     */ import org.dom4j.Document;
/*     */ import org.dom4j.DocumentException;
/*     */ import org.dom4j.QName;
/*     */ import org.dom4j.io.SAXReader;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class DavServlet
/*     */   extends ZimbraServlet
/*     */ {
/*     */   public static final String DAV_PATH = "/dav";
/*     */   private static Map<String, DavMethod> sMethods;
/*     */   
/*     */   public void init()
/*     */     throws ServletException
/*     */   {
/* 113 */     super.init();
/* 114 */     sMethods = new HashMap();
/* 115 */     addMethod(new Copy());
/* 116 */     addMethod(new Delete());
/* 117 */     addMethod(new Get());
/* 118 */     addMethod(new Head());
/* 119 */     addMethod(new Lock());
/* 120 */     addMethod(new MkCol());
/* 121 */     addMethod(new Move());
/* 122 */     addMethod(new Options());
/* 123 */     addMethod(new Post());
/* 124 */     addMethod(new Put());
/* 125 */     addMethod(new PropFind());
/* 126 */     addMethod(new PropPatch());
/* 127 */     addMethod(new Unlock());
/* 128 */     addMethod(new MkCalendar());
/* 129 */     addMethod(new Report());
/* 130 */     addMethod(new Acl());
/*     */   }
/*     */   
/*     */   protected void addMethod(DavMethod method) {
/* 134 */     sMethods.put(method.getName(), method);
/*     */   }
/*     */   
/*     */   public static void setAllowHeader(HttpServletResponse resp) {
/* 138 */     Set<String> methods = sMethods.keySet();
/* 139 */     StringBuilder buf = new StringBuilder();
/* 140 */     for (String method : methods) {
/* 141 */       if (buf.length() > 0)
/* 142 */         buf.append(", ");
/* 143 */       buf.append(method);
/*     */     }
/* 145 */     DavMethod.setResponseHeader(resp, "Allow", buf.toString());
/*     */   }
/*     */   
/* 148 */   static enum RequestType { password,  authtoken,  both,  none;
/*     */     
/*     */     private RequestType() {} }
/* 151 */   private RequestType getAllowedRequestType(HttpServletRequest req) { if (!super.isRequestOnAllowedPort(req))
/* 152 */       return RequestType.none;
/* 153 */     Server server = null;
/*     */     try {
/* 155 */       server = Provisioning.getInstance().getLocalServer();
/*     */     } catch (Exception e) {
/* 157 */       return RequestType.none;
/*     */     }
/* 159 */     boolean allowPassword = server.getBooleanAttr("zimbraCalendarCalDavClearTextPasswordEnabled", true);
/* 160 */     int sslPort = server.getIntAttr("zimbraMailSSLPort", 443);
/* 161 */     int mailPort = server.getIntAttr("zimbraMailPort", 80);
/* 162 */     int incomingPort = req.getLocalPort();
/* 163 */     if (incomingPort == sslPort)
/* 164 */       return RequestType.both;
/* 165 */     if ((incomingPort == mailPort) && (allowPassword)) {
/* 166 */       return RequestType.both;
/*     */     }
/* 168 */     return RequestType.authtoken;
/*     */   }
/*     */   
/*     */   private void logRequestInfo(HttpServletRequest req) {
/* 172 */     if (!ZimbraLog.dav.isDebugEnabled()) {
/* 173 */       return;
/*     */     }
/* 175 */     StringBuilder hdrs = new StringBuilder();
/* 176 */     hdrs.append("DAV REQUEST:\n");
/* 177 */     hdrs.append(req.getMethod()).append(" ").append(req.getRequestURL().toString()).append(" ").append(req.getProtocol());
/*     */     
/* 179 */     Enumeration paramNames = req.getParameterNames();
/* 180 */     if ((paramNames != null) && (paramNames.hasMoreElements())) {
/* 181 */       hdrs.append("\nDAV REQUEST PARAMS:");
/* 182 */       while (paramNames.hasMoreElements()) {
/* 183 */         String paramName = (String)paramNames.nextElement();
/* 184 */         if (paramName.contains("Auth")) {
/* 185 */           hdrs.append("\n").append(paramName).append("=*** REPLACED ***");
/*     */         }
/*     */         else {
/* 188 */           String[] params = req.getParameterValues(paramName);
/* 189 */           if (params != null) {
/* 190 */             for (String param : params) {
/* 191 */               hdrs.append("\n").append(paramName).append("=").append(param);
/*     */             }
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */ 
/* 199 */     Enumeration namesEn = req.getHeaderNames();
/* 200 */     if ((namesEn != null) && (namesEn.hasMoreElements())) {
/* 201 */       hdrs.append("\nDAV REQUEST HEADERS:");
/* 202 */       while (namesEn.hasMoreElements()) {
/* 203 */         String hdrName = (String)namesEn.nextElement();
/* 204 */         if ((hdrName.contains("Auth")) || (hdrName.contains("Cookie"))) {
/* 205 */           hdrs.append("\n").append(hdrName).append(": *** REPLACED ***");
/*     */         }
/*     */         else {
/* 208 */           Enumeration vals = req.getHeaders(hdrName);
/* 209 */           while (vals.hasMoreElements())
/* 210 */             hdrs.append("\n").append(hdrName).append(": ").append((String)vals.nextElement());
/*     */         }
/*     */       }
/*     */     }
/* 214 */     ZimbraLog.dav.debug(hdrs.toString());
/*     */   }
/*     */   
/*     */   public static StringBuilder addResponseHeaderLoggingInfo(HttpServletResponse resp, StringBuilder sb) {
/* 218 */     if (!ZimbraLog.dav.isDebugEnabled()) {
/* 219 */       return sb;
/*     */     }
/* 221 */     sb.append("DAV RESPONSE:\n");
/* 222 */     String statusLine = (String)DavResponse.sStatusTextMap.get(Integer.valueOf(resp.getStatus()));
/* 223 */     if (statusLine != null) {
/* 224 */       sb.append(statusLine);
/*     */     } else {
/* 226 */       sb.append("HTTP/1.1 ").append(resp.getStatus());
/*     */     }
/* 228 */     Collection<String> hdrNames = resp.getHeaderNames();
/* 229 */     Iterator i$; if ((hdrNames != null) && (!hdrNames.isEmpty()))
/* 230 */       for (i$ = hdrNames.iterator(); i$.hasNext();) { hdrName = (String)i$.next();
/* 231 */         if ((hdrName.contains("Auth")) || (hdrName.contains("Cookie"))) {
/* 232 */           sb.append("\n").append(hdrName).append(": *** REPLACED ***");
/*     */         }
/*     */         else {
/* 235 */           Collection<String> vals = resp.getHeaders(hdrName);
/* 236 */           for (String val : vals)
/* 237 */             sb.append("\n").append(hdrName).append(": ").append(val);
/*     */         }
/*     */       }
/*     */     String hdrName;
/* 241 */     sb.append("\n\n");
/* 242 */     return sb;
/*     */   }
/*     */   
/*     */   protected static void logResponseInfo(HttpServletResponse resp) {
/* 246 */     if (!ZimbraLog.dav.isDebugEnabled()) {
/* 247 */       return;
/*     */     }
/* 249 */     StringBuilder hdrs = addResponseHeaderLoggingInfo(resp, new StringBuilder());
/* 250 */     ZimbraLog.dav.debug(hdrs.toString());
/*     */   }
/*     */   
/*     */   private void sendError(HttpServletResponse resp, int statusCode, String logMsg, Exception e, Log.Level level) throws IOException
/*     */   {
/*     */     try {
/* 256 */       resp.sendError(statusCode);
/* 257 */       if (ZimbraLog.dav.isEnabledFor(level)) {
/* 258 */         if (e == null) {
/* 259 */           ZimbraLog.dav.log(level, "%s.  Sending HTTP Error - StatusCode %s", new Object[] { logMsg, Integer.valueOf(statusCode) });
/*     */         } else {
/* 261 */           ZimbraLog.dav.log(level, "%s.  Sending HTTP Error - StatusCode %s", logMsg, Integer.valueOf(statusCode), e);
/*     */         }
/*     */       }
/*     */     } catch (Exception except) {
/* 265 */       if (e == null) {
/* 266 */         ZimbraLog.dav.log(level, "2nd call to sendError will be ignored %s. StatusCode=%s newException=%s:%s", new Object[] { logMsg, Integer.valueOf(statusCode), except.getClass().getName(), except.getMessage() });
/*     */       }
/*     */       else
/*     */       {
/* 270 */         ZimbraLog.dav.log(level, "2nd call to sendError will be ignored %s. StatusCode=%s 1st exception=%s newException=%s:%s", new Object[] { logMsg, Integer.valueOf(statusCode), e.getMessage(), except.getClass().getName(), except.getMessage() });
/*     */       }
/*     */       
/*     */ 
/* 274 */       throw except;
/*     */     }
/*     */   }
/*     */   
/*     */   private void sendError(HttpServletResponse resp, int statusCode, String logMsg, Exception e) throws IOException {
/* 279 */     sendError(resp, statusCode, logMsg, e, Log.Level.error);
/*     */   }
/*     */   
/*     */   public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
/*     */   {
/* 284 */     ZimbraLog.clearContext();
/* 285 */     addRemoteIpToLoggingContext(req);
/* 286 */     ZimbraLog.addUserAgentToContext(req.getHeader("User-Agent"));
/*     */     
/*     */ 
/* 289 */     String rangeHeader = req.getHeader("RANGE");
/* 290 */     if (null != rangeHeader) {
/* 291 */       sendError(resp, 400, "Range header not supported", null, Log.Level.debug);
/* 292 */       return;
/*     */     }
/*     */     
/* 295 */     RequestType rtype = getAllowedRequestType(req);
/* 296 */     ZimbraLog.dav.debug("Allowable request types %s", new Object[] { rtype });
/*     */     
/* 298 */     if (rtype == RequestType.none) {
/* 299 */       sendError(resp, 406, "Not an allowed request type", null, Log.Level.debug);
/* 300 */       return;
/*     */     }
/*     */     
/* 303 */     logRequestInfo(req);
/* 304 */     Account authUser = null;
/*     */     DavContext ctxt;
/*     */     try {
/* 307 */       AuthToken at = AuthProvider.getAuthToken(req, false);
/* 308 */       if ((at != null) && ((at.isExpired()) || (!at.isRegistered()))) {
/* 309 */         at = null;
/*     */       }
/* 311 */       if ((at != null) && ((rtype == RequestType.both) || (rtype == RequestType.authtoken))) {
/* 312 */         authUser = Provisioning.getInstance().get(Key.AccountBy.id, at.getAccountId());
/* 313 */       } else if ((at == null) && ((rtype == RequestType.both) || (rtype == RequestType.password))) {
/* 314 */         AuthUtil.AuthResult result = AuthUtil.basicAuthRequest(req, resp, true, this);
/* 315 */         if (result.sendErrorCalled) {
/* 316 */           logResponseInfo(resp);
/* 317 */           return;
/*     */         }
/* 319 */         authUser = result.authorizedAccount;
/*     */       }
/* 321 */       if (authUser == null) {
/*     */         try {
/* 323 */           sendError(resp, 401, "Authentication failed", null, Log.Level.debug);
/*     */         } catch (Exception e) {}
/* 325 */         return;
/*     */       }
/* 327 */       ZimbraLog.addToContext("aname", authUser.getName());
/* 328 */       ctxt = new DavContext(req, resp, authUser);
/*     */     } catch (AuthTokenException e) {
/* 330 */       sendError(resp, 500, "error getting authenticated user", e);
/* 331 */       return;
/*     */     } catch (ServiceException e) {
/* 333 */       sendError(resp, 500, "error getting authenticated user", e);
/* 334 */       return;
/*     */     }
/*     */     
/* 337 */     DavMethod method = (DavMethod)sMethods.get(req.getMethod());
/* 338 */     if (method == null) {
/* 339 */       setAllowHeader(resp);
/* 340 */       sendError(resp, 405, "Not an allowed method", null, Log.Level.debug);
/* 341 */       return;
/*     */     }
/*     */     
/* 344 */     long t0 = System.currentTimeMillis();
/*     */     
/* 346 */     CacheStates cache = null;
/*     */     try {
/* 348 */       if (ZimbraLog.dav.isDebugEnabled()) {
/*     */         try {
/* 350 */           FileUploadServlet.Upload upload = ctxt.getUpload();
/* 351 */           if ((upload.getSize() > 0L) && (upload.getContentType().startsWith("text")) && 
/* 352 */             (ZimbraLog.dav.isDebugEnabled())) {
/* 353 */             StringBuilder logMsg = new StringBuilder("REQUEST\n").append(new String(ByteUtil.readInput(upload.getInputStream(), -1, 20480), "UTF-8"));
/*     */             
/* 355 */             ZimbraLog.dav.debug(logMsg.toString());
/*     */           }
/*     */         }
/*     */         catch (DavException de) {
/* 359 */           throw de;
/*     */         } catch (Exception e) {
/* 361 */           ZimbraLog.dav.debug("ouch", e);
/*     */         }
/*     */       }
/* 364 */       cache = checkCachedResponse(ctxt, authUser);
/* 365 */       if ((!ctxt.isResponseSent()) && (!isProxyRequest(ctxt, method)))
/*     */       {
/* 367 */         method.checkPrecondition(ctxt);
/* 368 */         method.handle(ctxt);
/* 369 */         method.checkPostcondition(ctxt);
/* 370 */         if (!ctxt.isResponseSent()) {
/* 371 */           resp.setStatus(ctxt.getStatus());
/*     */         }
/*     */       }
/* 374 */       if (!ctxt.isResponseSent())
/* 375 */         logResponseInfo(resp);
/*     */     } catch (DavException e) {
/*     */       long t1;
/* 378 */       if (((e.getCause() instanceof MailServiceException.NoSuchItemException)) || (e.getStatus() == 404))
/*     */       {
/* 380 */         ZimbraLog.dav.info(ctxt.getUri() + " not found");
/* 381 */       } else if ((e.getStatus() == 302) || (e.getStatus() == 301))
/*     */       {
/* 383 */         ZimbraLog.dav.info("sending redirect");
/*     */       }
/*     */       try {
/* 386 */         if (e.isStatusSet()) {
/* 387 */           resp.setStatus(e.getStatus());
/* 388 */           if (e.hasErrorMessage())
/* 389 */             e.writeErrorMsg(resp.getOutputStream());
/* 390 */           if (ZimbraLog.dav.isDebugEnabled()) {
/* 391 */             ZimbraLog.dav.info("sending http error %d because: %s", Integer.valueOf(e.getStatus()), e.getMessage(), e);
/*     */           } else {
/* 393 */             ZimbraLog.dav.info("sending http error %d because: %s", new Object[] { Integer.valueOf(e.getStatus()), e.getMessage() });
/*     */           }
/* 395 */           if (e.getCause() != null)
/* 396 */             ZimbraLog.dav.debug("exception: ", e.getCause());
/*     */         } else {
/* 398 */           sendError(resp, 500, "error handling method " + method.getName(), e);
/*     */         }
/*     */       }
/*     */       catch (IllegalStateException ise) {
/* 402 */         ZimbraLog.dav.debug("can't write error msg", ise);
/*     */       } } catch (ServiceException e) { long t1;
/*     */       long t1;
/* 405 */       if ((e instanceof MailServiceException.NoSuchItemException)) {
/* 406 */         sendError(resp, 404, ctxt.getUri() + " not found", null, Log.Level.info);
/*     */       }
/*     */       else
/* 409 */         sendError(resp, 500, "error handling method " + method.getName(), e);
/*     */     } catch (Exception e) {
/*     */       try { long t1;
/* 412 */         sendError(resp, 500, "error handling method " + method.getName(), e);
/*     */       } catch (Exception ex) {}
/*     */     } finally {
/*     */       long t1;
/* 416 */       long t1 = System.currentTimeMillis();
/* 417 */       ZimbraLog.dav.info("DavServlet operation " + method.getName() + " to " + req.getPathInfo() + " (depth: " + ctxt.getDepth().name() + ") finished in " + (t1 - t0) + "ms");
/*     */       
/* 419 */       if (cache != null)
/* 420 */         cacheCleanUp(ctxt, cache);
/* 421 */       ctxt.cleanup();
/*     */     }
/*     */   }
/*     */   
/*     */   public static String getDavUrl(String user) throws DavException, ServiceException {
/* 426 */     Provisioning prov = Provisioning.getInstance();
/* 427 */     Account account = prov.get(Key.AccountBy.name, user);
/* 428 */     if (account == null)
/* 429 */       throw new DavException("unknown user " + user, 404, null);
/* 430 */     return getServiceUrl(account, "/dav");
/*     */   }
/*     */   
/*     */   private boolean isCtagRequest(DavContext ctxt) throws DavException
/*     */   {
/* 435 */     String httpMethod = ctxt.getRequest().getMethod();
/* 436 */     if (("PROPFIND".equalsIgnoreCase(httpMethod)) && (ctxt.hasRequestMessage())) {
/* 437 */       Document doc = ctxt.getRequestMessage();
/* 438 */       org.dom4j.Element top = doc.getRootElement();
/* 439 */       if ((top == null) || (!top.getQName().equals(DavElements.E_PROPFIND)))
/* 440 */         return false;
/* 441 */       org.dom4j.Element prop = top.element(DavElements.E_PROP);
/* 442 */       if (prop == null)
/* 443 */         return false;
/* 444 */       Iterator<?> iter = prop.elementIterator();
/* 445 */       while (iter.hasNext()) {
/* 446 */         prop = (org.dom4j.Element)iter.next();
/* 447 */         if (prop.getQName().equals(DavElements.E_GETCTAG))
/* 448 */           return true;
/*     */       }
/*     */     }
/* 451 */     return false;
/*     */   }
/*     */   
/*     */   private static class CacheStates {
/* 455 */     boolean ctagCacheEnabled = MemcachedConnector.isConnected();
/* 456 */     boolean gzipAccepted = false;
/* 457 */     boolean cacheThisCtagResponse = false;
/* 458 */     CtagResponseCache.CtagResponseCacheKey ctagCacheKey = null;
/* 459 */     String acctVerSnapshot = null;
/* 460 */     Map<Integer, String> ctagsSnapshot = null;
/* 461 */     CtagResponseCache ctagResponseCache = null;
/*     */   }
/*     */   
/*     */   private CacheStates checkCachedResponse(DavContext ctxt, Account authUser) throws IOException, DavException, ServiceException {
/* 465 */     CacheStates cache = new CacheStates(null);
/*     */     
/*     */ 
/* 468 */     if ((cache.ctagCacheEnabled) && (isCtagRequest(ctxt))) {
/* 469 */       cache.ctagResponseCache = CalendarCacheManager.getInstance().getCtagResponseCache();
/* 470 */       cache.gzipAccepted = ctxt.isGzipAccepted();
/* 471 */       String targetUser = ctxt.getUser();
/* 472 */       Account targetAcct = Provisioning.getInstance().get(Key.AccountBy.name, targetUser);
/* 473 */       boolean ownAcct = (targetAcct != null) && (targetAcct.getId().equals(authUser.getId()));
/* 474 */       String parentPath = ctxt.getPath();
/* 475 */       DavContext.KnownUserAgent knownUA = ctxt.getKnownUserAgent();
/*     */       
/* 477 */       if ((ownAcct) && (knownUA != null) && (parentPath != null)) {
/* 478 */         AccountKey accountKey = new AccountKey(targetAcct.getId());
/* 479 */         AccountCtags allCtagsData = CalendarCacheManager.getInstance().getCtags(accountKey);
/*     */         
/* 481 */         if (allCtagsData != null) {
/* 482 */           boolean validRoot = true;
/* 483 */           int rootFolderId = 1;
/* 484 */           if (!"/".equals(parentPath)) {
/* 485 */             CtagInfo calInfoRoot = allCtagsData.getByPath(parentPath);
/* 486 */             if (calInfoRoot != null) {
/* 487 */               rootFolderId = calInfoRoot.getId();
/*     */             } else
/* 489 */               validRoot = false;
/*     */           }
/* 491 */           if (validRoot)
/*     */           {
/* 493 */             cache.ctagCacheKey = new CtagResponseCache.CtagResponseCacheKey(targetAcct.getId(), knownUA.toString(), rootFolderId);
/* 494 */             CtagResponseCache.CtagResponseCacheValue ctagResponse = cache.ctagResponseCache.get(cache.ctagCacheKey);
/* 495 */             if (ctagResponse != null)
/*     */             {
/*     */ 
/* 498 */               String currentCalListVer = allCtagsData.getVersion();
/* 499 */               if (currentCalListVer.equals(ctagResponse.getVersion()))
/*     */               {
/* 501 */                 boolean cacheHit = true;
/* 502 */                 Map<Integer, String> oldCtags = ctagResponse.getCtags();
/*     */                 
/* 504 */                 for (Map.Entry<Integer, String> entry : oldCtags.entrySet()) {
/* 505 */                   int calFolderId = ((Integer)entry.getKey()).intValue();
/* 506 */                   String ctag = (String)entry.getValue();
/* 507 */                   CtagInfo calInfoCurr = allCtagsData.getById(calFolderId);
/* 508 */                   if (calInfoCurr == null)
/*     */                   {
/*     */ 
/* 511 */                     cacheHit = false;
/* 512 */                     break;
/*     */                   }
/* 514 */                   if (!ctag.equals(calInfoCurr.getCtag()))
/*     */                   {
/* 516 */                     cacheHit = false;
/* 517 */                     break;
/*     */                   }
/*     */                 }
/* 520 */                 if (cacheHit) {
/* 521 */                   ZimbraLog.dav.debug("CTAG REQUEST CACHE HIT");
/*     */                   
/* 523 */                   ctxt.setStatus(207);
/* 524 */                   HttpServletResponse response = ctxt.getResponse();
/* 525 */                   response.setStatus(ctxt.getStatus());
/* 526 */                   response.setContentType("text/xml; charset=\"UTF-8\"");
/* 527 */                   byte[] respData = ctagResponse.getResponseBody();
/* 528 */                   response.setContentLength(ctagResponse.getRawLength());
/*     */                   
/* 530 */                   byte[] unzipped = null;
/* 531 */                   if ((ZimbraLog.dav.isDebugEnabled()) || ((ctagResponse.isGzipped()) && (!cache.gzipAccepted))) {
/* 532 */                     if (ctagResponse.isGzipped()) {
/* 533 */                       ByteArrayInputStream bais = new ByteArrayInputStream(respData);
/* 534 */                       ByteArrayOutputStream baos = new ByteArrayOutputStream();
/* 535 */                       GZIPInputStream gzis = null;
/*     */                       try {
/* 537 */                         gzis = new GZIPInputStream(bais, respData.length);
/* 538 */                         ByteUtil.copy(gzis, false, baos, true);
/*     */                       } finally {
/* 540 */                         ByteUtil.closeStream(gzis);
/*     */                       }
/* 542 */                       unzipped = baos.toByteArray();
/*     */                     } else {
/* 544 */                       unzipped = respData;
/*     */                     }
/* 546 */                     if (ZimbraLog.dav.isDebugEnabled()) {
/* 547 */                       ZimbraLog.dav.debug("RESPONSE:\n" + new String(unzipped, "UTF-8"));
/*     */                     }
/*     */                   }
/* 550 */                   if (!ctagResponse.isGzipped()) {
/* 551 */                     response.getOutputStream().write(respData);
/*     */                   }
/* 553 */                   else if (cache.gzipAccepted) {
/* 554 */                     response.addHeader("Content-Encoding", "gzip");
/* 555 */                     response.getOutputStream().write(respData);
/*     */                   } else {
/* 557 */                     assert (unzipped != null);
/* 558 */                     response.getOutputStream().write(unzipped);
/*     */                   }
/*     */                   
/*     */ 
/*     */ 
/* 563 */                   ctxt.responseSent();
/*     */                 }
/*     */               }
/*     */             }
/*     */             
/* 568 */             if (!ctxt.isResponseSent())
/*     */             {
/*     */ 
/*     */ 
/* 572 */               cache.cacheThisCtagResponse = true;
/* 573 */               cache.acctVerSnapshot = allCtagsData.getVersion();
/* 574 */               cache.ctagsSnapshot = new HashMap();
/* 575 */               Collection<CtagInfo> childCals = allCtagsData.getChildren(rootFolderId);
/* 576 */               if (rootFolderId != 1) {
/* 577 */                 CtagInfo ctagRoot = allCtagsData.getById(rootFolderId);
/* 578 */                 if (ctagRoot != null)
/* 579 */                   cache.ctagsSnapshot.put(Integer.valueOf(rootFolderId), ctagRoot.getCtag());
/*     */               }
/* 581 */               for (CtagInfo calInfo : childCals) {
/* 582 */                 cache.ctagsSnapshot.put(Integer.valueOf(calInfo.getId()), calInfo.getCtag());
/*     */               }
/*     */             }
/*     */           }
/*     */         }
/* 587 */         if (!ctxt.isResponseSent())
/* 588 */           ZimbraLog.dav.debug("CTAG REQUEST CACHE MISS");
/*     */       }
/*     */     }
/* 591 */     return cache;
/*     */   }
/*     */   
/* 594 */   private void cacheCleanUp(DavContext ctxt, CacheStates cache) throws IOException { if ((cache.ctagCacheEnabled) && (cache.cacheThisCtagResponse) && (ctxt.getStatus() == 207)) {
/* 595 */       assert ((cache.ctagCacheKey != null) && (cache.acctVerSnapshot != null) && (!cache.ctagsSnapshot.isEmpty()));
/* 596 */       DavResponse dresp = ctxt.getDavResponse();
/* 597 */       ByteArrayOutputStream baosRaw = null;
/*     */       try {
/* 599 */         baosRaw = new ByteArrayOutputStream();
/* 600 */         dresp.writeTo(baosRaw);
/*     */       } finally {
/* 602 */         ByteUtil.closeStream(baosRaw);
/*     */       }
/* 604 */       byte[] respData = baosRaw.toByteArray();
/* 605 */       int rawLen = respData.length;
/*     */       
/* 607 */       boolean forceGzip = true;
/*     */       
/* 609 */       boolean responseGzipped = (forceGzip) || (cache.gzipAccepted);
/* 610 */       if (responseGzipped) {
/* 611 */         ByteArrayOutputStream baosGzipped = new ByteArrayOutputStream();
/* 612 */         GZIPOutputStream gzos = null;
/*     */         try {
/* 614 */           gzos = new GZIPOutputStream(baosGzipped);
/* 615 */           gzos.write(respData);
/*     */         } finally {
/* 617 */           ByteUtil.closeStream(gzos);
/*     */         }
/* 619 */         respData = baosGzipped.toByteArray();
/*     */       }
/*     */       
/* 622 */       CtagResponseCache.CtagResponseCacheValue ctagCacheVal = new CtagResponseCache.CtagResponseCacheValue(respData, rawLen, responseGzipped, cache.acctVerSnapshot, cache.ctagsSnapshot);
/*     */       try
/*     */       {
/* 625 */         cache.ctagResponseCache.put(cache.ctagCacheKey, ctagCacheVal);
/*     */       } catch (ServiceException e) {
/* 627 */         ZimbraLog.dav.warn("Unable to cache ctag response", e);
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */ 
/* 633 */   private static String[] PROXY_REQUEST_HEADERS = { "DAV", "Depth", "Content-Type", "ETag", "If-Match", "Overwrite", "Destination" };
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 643 */   private static String[] PROXY_RESPONSE_HEADERS = { "DAV", "Allow", "Content-Type", "ETag" };
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   private boolean isProxyRequest(DavContext ctxt, DavMethod m)
/*     */     throws IOException, DavException, ServiceException
/*     */   {
/* 651 */     Provisioning prov = Provisioning.getInstance();
/* 652 */     ItemId target = null;
/* 653 */     String extraPath = null;
/* 654 */     String requestPath = ctxt.getPath();
/*     */     try {
/* 656 */       if (ctxt.getUser() == null)
/* 657 */         return false;
/* 658 */       if ((requestPath == null) || (requestPath.length() < 2))
/* 659 */         return false;
/* 660 */       Account account = prov.getAccountByName(ctxt.getUser());
/* 661 */       if (account == null)
/* 662 */         return false;
/* 663 */       Mailbox mbox = MailboxManager.getInstance().getMailboxByAccount(account);
/* 664 */       Pair<Folder, String> match = mbox.getFolderByPathLongestMatch(ctxt.getOperationContext(), 1, requestPath);
/* 665 */       Folder targetFolder = (Folder)match.getFirst();
/* 666 */       if (!(targetFolder instanceof Mountpoint))
/* 667 */         return false;
/* 668 */       Mountpoint mp = (Mountpoint)targetFolder;
/* 669 */       target = new ItemId(mp.getOwnerId(), mp.getRemoteId());
/* 670 */       extraPath = (String)match.getSecond();
/*     */     } catch (ServiceException e) {
/* 672 */       ZimbraLog.dav.debug("can't get path", e);
/* 673 */       return false;
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 680 */     if ((extraPath == null) && (((m.getName().equals("PROPFIND")) && (ctxt.getDepth() == DavContext.Depth.zero)) || (m.getName().equals("PROPPATCH")) || (m.getName().equals("DELETE"))))
/*     */     {
/*     */ 
/*     */ 
/* 684 */       return false;
/*     */     }
/* 686 */     String prefix = ctxt.getPath();
/* 687 */     if (extraPath != null)
/* 688 */       prefix = prefix.substring(0, prefix.indexOf(extraPath));
/* 689 */     prefix = HttpUtil.urlEscape("/dav/" + ctxt.getUser() + prefix);
/*     */     
/* 691 */     if (!prefix.endsWith("/")) {
/* 692 */       prefix = prefix + "/";
/*     */     }
/*     */     
/* 695 */     Account acct = prov.getAccountById(target.getAccountId());
/* 696 */     if (acct == null)
/* 697 */       return false;
/* 698 */     Server server = prov.getServer(acct);
/* 699 */     if (server == null) {
/* 700 */       return false;
/*     */     }
/*     */     
/* 703 */     AuthToken authToken = AuthProvider.getAuthToken(ctxt.getAuthAccount());
/* 704 */     ZMailbox.Options zoptions = new ZMailbox.Options(authToken.toZAuthToken(), AccountUtil.getSoapUri(acct));
/* 705 */     zoptions.setNoSession(true);
/* 706 */     zoptions.setTargetAccount(target.getAccountId());
/* 707 */     zoptions.setTargetAccountBy(Key.AccountBy.id);
/* 708 */     ZMailbox zmbx = ZMailbox.getMailbox(zoptions);
/* 709 */     ZFolder f = zmbx.getFolderById("" + target.toString());
/* 710 */     if (f == null)
/* 711 */       return false;
/* 712 */     String path = f.getPath();
/* 713 */     String newPrefix = HttpUtil.urlEscape("/dav/" + acct.getName() + f.getPath());
/*     */     
/* 715 */     if (ctxt.hasRequestMessage())
/*     */     {
/* 717 */       Document req = ctxt.getRequestMessage();
/* 718 */       for (Object hrefObj : req.getRootElement().elements(DavElements.E_HREF)) {
/* 719 */         if ((hrefObj instanceof org.dom4j.Element))
/*     */         {
/* 721 */           org.dom4j.Element href = (org.dom4j.Element)hrefObj;
/* 722 */           String v = href.getText();
/*     */           
/* 724 */           href.setText(newPrefix + "/" + v.substring(v.lastIndexOf('/') + 1));
/*     */         }
/*     */       }
/*     */     }
/*     */     
/* 729 */     String url = getProxyUrl(ctxt.getRequest(), server, "/dav") + HttpUtil.urlEscape(new StringBuilder().append("/").append(acct.getName()).append(path).append("/").append(extraPath == null ? "" : extraPath).toString());
/* 730 */     HttpState state = new HttpState();
/* 731 */     authToken.encode(state, false, server.getAttr("zimbraServiceHostname"));
/* 732 */     HttpClient client = ZimbraHttpConnectionManager.getInternalHttpConnMgr().newHttpClient();
/* 733 */     client.setState(state);
/* 734 */     HttpMethod method = m.toHttpMethod(ctxt, url);
/* 735 */     for (String h : PROXY_REQUEST_HEADERS) {
/* 736 */       String hval = ctxt.getRequest().getHeader(h);
/* 737 */       if (hval != null)
/* 738 */         method.addRequestHeader(h, hval);
/*     */     }
/* 740 */     int statusCode = HttpClientUtil.executeMethod(client, method);
/* 741 */     for (String h : PROXY_RESPONSE_HEADERS) {
/* 742 */       for (Header hval : method.getResponseHeaders(h)) {
/* 743 */         ctxt.getResponse().addHeader(h, hval.getValue());
/*     */       }
/*     */     }
/* 746 */     ctxt.getResponse().setStatus(statusCode);
/* 747 */     ctxt.setStatus(statusCode);
/* 748 */     InputStream in = method.getResponseBodyAsStream();
/* 749 */     switch (statusCode)
/*     */     {
/*     */     case 207: 
/*     */       try {
/* 753 */         Document response = com.zimbra.common.soap.Element.getSAXReader().read(in);
/* 754 */         org.dom4j.Element top = response.getRootElement();
/* 755 */         for (Object responseObj : top.elements(DavElements.E_RESPONSE))
/* 756 */           if ((responseObj instanceof org.dom4j.Element))
/*     */           {
/* 758 */             org.dom4j.Element href = ((org.dom4j.Element)responseObj).element(DavElements.E_HREF);
/* 759 */             String v = href.getText();
/* 760 */             if (v.startsWith(newPrefix))
/* 761 */               href.setText(prefix + v.substring(newPrefix.length() + 1));
/*     */           }
/* 763 */         if (ZimbraLog.dav.isDebugEnabled())
/* 764 */           ZimbraLog.dav.debug("PROXY RESPONSE:\n" + new String(DomUtil.getBytes(response), "UTF-8"));
/* 765 */         DomUtil.writeDocumentToStream(response, ctxt.getResponse().getOutputStream());
/* 766 */         ctxt.responseSent();
/*     */       } catch (DocumentException e) {
/* 768 */         ZimbraLog.dav.warn("proxy request failed", e);
/* 769 */         return false;
/*     */       }
/*     */     
/*     */     default: 
/* 773 */       if (in != null)
/* 774 */         ByteUtil.copy(in, true, ctxt.getResponse().getOutputStream(), false);
/* 775 */       ctxt.responseSent();
/*     */     }
/*     */     
/* 778 */     return true;
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/dav/service/DavServlet.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */